package com.charging.system.service.impl;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.charging.common.WePublicUtils;
import com.charging.common.constant.Constants;
import com.charging.common.exception.UtilException;
import com.charging.common.utils.DateUtils;
import com.charging.common.utils.Threads;
import com.charging.common.utils.http.HttpUtils;
import com.charging.system.domain.KxCard;
import com.charging.system.domain.KxChargingRecord;
import com.charging.system.domain.KxUserInfo;
import com.charging.system.service.IKxCardService;
import com.charging.system.service.IKxChargingRecordService;
import com.charging.system.service.IKxUserInfoService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.pl.REGON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.charging.system.mapper.KxEquipMapper;
import com.charging.system.domain.KxEquip;
import com.charging.system.service.IKxEquipService;

import javax.annotation.Resource;

/**
 * 设备管理Service业务层处理
 *
 * @author charging
 * @date 2024-04-12
 */
@Service
public class KxEquipServiceImpl implements IKxEquipService
{
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Resource
    private KxEquipMapper kxEquipMapper;
    @Resource
    private IKxChargingRecordService kxChargingRecordService;
    @Resource
    private IKxUserInfoService kxUserInfoService;
    @Resource
    private IKxCardService kxCardService;
    @Resource
    private WePublicUtils wePublicUtils;

    private static ExecutorService executorService = Executors.newSingleThreadExecutor();

    /**
     * 根据设备编号查询设备信息
     * @param equipNo
     * @return
     */
    @Override
    public KxEquip getKxEquipByEquipNo(String equipNo) {
        return kxEquipMapper.getKxEquipByEquipNo(equipNo);
    }

    /**
     * 查询设备管理
     *
     * @param equipId 设备管理主键
     * @return 设备管理
     */
    @Override
    public KxEquip selectKxEquipByEquipId(Long equipId)
    {
        return kxEquipMapper.selectKxEquipByEquipId(equipId);
    }

    /**
     * 查询设备管理列表
     *
     * @param kxEquip 设备管理
     * @return 设备管理
     */
    @Override
    public List<KxEquip> selectKxEquipList(KxEquip kxEquip)
    {
        return kxEquipMapper.selectKxEquipList(kxEquip);
    }

    /**
     * 新增设备管理
     *
     * @param kxEquip 设备管理
     * @return 结果
     */
    @Override
    public int insertKxEquip(KxEquip kxEquip)
    {
        kxEquip.setCreateTime(DateUtils.getNowDate());
        return kxEquipMapper.insertKxEquip(kxEquip);
    }

    /**
     * 修改设备管理
     *
     * @param kxEquip 设备管理
     * @return 结果
     */
    @Override
    public int updateKxEquip(KxEquip kxEquip)
    {
        kxEquip.setUpdateTime(DateUtils.getNowDate());
        return kxEquipMapper.updateKxEquip(kxEquip);
    }

    /**
     * 批量删除设备管理
     *
     * @param equipIds 需要删除的设备管理主键
     * @return 结果
     */
    @Override
    public int deleteKxEquipByEquipIds(Long[] equipIds)
    {
        return kxEquipMapper.deleteKxEquipByEquipIds(equipIds);
    }

    /**
     * 删除设备管理信息
     *
     * @param equipId 设备管理主键
     * @return 结果
     */
    @Override
    public int deleteKxEquipByEquipId(Long equipId)
    {
        return kxEquipMapper.deleteKxEquipByEquipId(equipId);
    }

    /**
     * 通过设备编号更新设备信息
     * @param kxEquip
     * @return
     */
    @Override
    public int updateKxEquipByEquipNo(KxEquip kxEquip) {
        return kxEquipMapper.updateKxEquipByEquipNo(kxEquip);
    }
    /**
     * 打开关闭插座 请求
     * @param chargeno
     * @param channelno
     * @param type
     * @param time
     * @param chargingFullNotStop
     * @return
     * authKey (String): 请求认证Key[e101cddaefc26cfdaf368fd5c98ad76f]
     * cmd (String): 控制指令[setchannel]
     * chargeno (String): 设备编号[66660000]
     * channelno (int): 通道号[1~40]
     * result (String): 返回CODE[0 执行失败 ,1 执行成功, -1 解析失败,-2 {系统其他错误}]
     * resultInfo (String): 执行结果描述
     * type (int): 充电指令
     * endPower (String): 结束时功率[type=0 时有用]
     * endDegree (String): 结束时用电量[type=0 时有用]
     * eddyTime (String): 浮充时长[type=0 时有用]
     */
    @Override
    public JSONObject setChannel(String chargeno, String channelno, String type, String time, String chargingFullNotStop) {
        String param = "authKey="+Constants.AUTH_KEY+"&"+
                "cmd=setchannel&chargeno="+chargeno+"&"+
                "channelno="+channelno+"&type="+type+"&"+
                "time="+time +"&cfns="+chargingFullNotStop;

        String request = HttpUtils.sendGet(Constants.KEHANG_EQUIP_URL,param);
        try {
            JSONObject jsonObject = JSON.parseObject(request);
            return jsonObject;
        }catch (Exception e){
            logger.error("发送控制插座指令失败："+e.getMessage());
        }

        return null;
    }

    /*
    仅关闭插座
     */
    @Override
    public void onlyCloseChannel(String equipNo, String channelNo) {
        executorService.execute(new Runnable() {
            public void run() {
                System.out.println("1-Asynchronous task" +equipNo + " "+ channelNo);
                Threads.sleep(500);
                logger.info("设备号["+equipNo+"],通道号["+channelNo+"]:执行关闭插座");
                setChannel(equipNo,String.valueOf(channelNo), Constants.SetChannelState.close,"0","1");
                System.out.println("2-Asynchronous task" +equipNo + " "+ channelNo);
            }
        });
    }

    /**
     * 关闭插座后业务处理
     *  * 1、关闭插座操作
     *      * 2、更新充电状态为充电结束
     *      * 3、推送充电结束消息 ，异步进行。
     * @param equipNo
     * @param channelNo
     * @param endType 结束类型
     * @param Active 是否设备主动关闭， true 主动关闭  false 设备关闭
     */
    @Override
    public void closeChannel(String equipNo, int channelNo, int endType, boolean Active) {
        if(Active){
            JSONObject setChannelResp = setChannel(equipNo,String.valueOf(channelNo), Constants.SetChannelState.close,"0","0");
            if(ObjectUtils.isNotEmpty(setChannelResp)&& ObjectUtils.isNotEmpty(setChannelResp.get("chargeno"))){//插座正常反馈
                if("1".equals(setChannelResp.getString("result"))) { // 关闭插座成功
                    // 无论是否关闭插座都更新充电状态
                }else {
                    logger.info("{}","关闭插座失败，在异步关闭一次。之后不在处理");
                    this.onlyCloseChannel(equipNo,String.valueOf(channelNo));
                }
            }else{
                logger.info("{}","关闭插座失败，在异步关闭一次。之后不在处理");
                this.onlyCloseChannel(equipNo,String.valueOf(channelNo));
            }
        }
        KxEquip kxEquip = kxEquipMapper.getKxEquipByEquipNo(equipNo);
        KxChargingRecord kxChargingRecord1 = new KxChargingRecord();
        kxChargingRecord1.setEquipId(kxEquip.getEquipId());
        kxChargingRecord1.setChannelNo(channelNo);
        kxChargingRecord1.setChargingStatus(Constants.ChargingStatus.INTCHARGING);
        List<KxChargingRecord> kxChargingRecords = kxChargingRecordService.selectKxChargingRecordList(kxChargingRecord1);
        if(kxChargingRecords.size()==1){
            KxChargingRecord kxChargingRecord = kxChargingRecords.get(0);
            // 如果用户充电计费次数小于一分钟，消费金额小于5分钱则不计入消费金额
            if(kxChargingRecord.getNumTimes()<=1&&kxChargingRecord.getAmount().compareTo(new BigDecimal("0.05"))==-1){
                kxChargingRecord.setAmount(BigDecimal.ZERO);
            }else {
                kxChargingRecord.setAmount(kxChargingRecord.getAmount().setScale(2,BigDecimal.ROUND_UP));
                //扣除用户或电卡金额
                if(ObjectUtils.isNotEmpty(kxChargingRecord.getCardId())&&kxChargingRecord.getCardId().longValue()!=0L){// 电卡充电
                    //
                    KxCard kxCard = kxCardService.selectKxCardByCardId(kxChargingRecord.getCardId());
                    if(kxCard.getGive().compareTo(BigDecimal.ZERO)==1) { // 有赠送金额，先扣除赠送金额
                        BigDecimal give = kxCard.getGive().subtract(kxChargingRecord.getAmount());
                        if(give.compareTo(BigDecimal.ZERO)==-1){
                            kxCard.setGive(BigDecimal.ZERO);
                            kxCard.setBalance(kxCard.getBalance().add(give));
                        }else {
                            kxCard.setGive(give);
                        }
                    }else {// 没有赠送金额
                        kxCard.setBalance(kxCard.getBalance().subtract(kxChargingRecord.getAmount()));
                    }
                    int c = kxCardService.updateBalance(kxCard);
                }else{
                    KxUserInfo account = kxUserInfoService.selectKxUserInfoByUserId(kxChargingRecord.getUserId());
                    BigDecimal give_ =  account.getGive();
                    if(give_.compareTo(BigDecimal.ZERO)==1){ // 有赠送金额，先扣除赠送金额
                        BigDecimal give = give_.subtract(kxChargingRecord.getAmount());
                        if(give.compareTo(BigDecimal.ZERO)==-1){
                            account.setGive(BigDecimal.ZERO);
                            account.setBalance(account.getBalance().add(give));
                        }else {
                            account.setGive(give);
                        }
                    }else {// 没有赠送金额
                        account.setBalance(account.getBalance().subtract(kxChargingRecord.getAmount()));
                    }
                    int a = kxUserInfoService.updateBalance(account);
                }
            }
            kxChargingRecord.setEndType(endType);
            kxChargingRecord.setChargingStatus(Constants.ChargingStatus.ENDCHARGING);
            // 用户钱已经扣了先把记录的钱改了
            KxChargingRecord kxChargingRecordForAmount = new KxChargingRecord();
            kxChargingRecordForAmount.setChargingRecordId(kxChargingRecord.getChargingRecordId());
            kxChargingRecordForAmount.setAmount(kxChargingRecord.getAmount());
            kxChargingRecordForAmount.setEndType(kxChargingRecord.getEndType());
            kxChargingRecordForAmount.setChargingStatus(kxChargingRecord.getChargingStatus());
            kxChargingRecordService.updateKxChargingRecord(kxChargingRecordForAmount);
            // 进入充电结束逻辑,插入到备份表
            // 发送停止充电提醒
            SendMessage(kxChargingRecord);
        }else {
            throw new UtilException("充电记录不存在");
        }
    }

    /**
     * 用户充电结束推送，电卡不推动
     * 根据充电记录推送结束充电消息
     * @param kxChargingRecord
     */
    private void SendMessage(KxChargingRecord kxChargingRecord){
        KxUserInfo account = null;
        // 用户推送
        if(ObjectUtils.isNotEmpty(kxChargingRecord.getUserId())){
            account = kxUserInfoService.selectKxUserInfoByUserId(kxChargingRecord.getUserId());
        }else { // 电卡推送，如果电卡有绑定的账户号则推送给指定账户
            KxCard kxCard = kxCardService.selectKxCardByCardId(kxChargingRecord.getCardId());

        }
        if(null!=account){
            Long charge_minutes = ChronoUnit.MINUTES.between(
                    Instant.ofEpochMilli(kxChargingRecord.getCreateTime().getTime()),
                    Instant.ofEpochMilli(kxChargingRecord.getUpdateTime().getTime()));
            if(StringUtils.isNotEmpty(account.getOpenId())){
                wePublicUtils.sendWeMsgEndCharging(Constants.kxSystemInfo.getWxAppId(),Constants.kxSystemInfo.getWxAppSecret(),
                        Constants.kxSystemInfo.getWxTemplateUrl(),Constants.kxSystemInfo.getWxTemplateId(),"充电用户",account.getOpenId(),// 账户名、openid
                        kxChargingRecord.getEquipNo()+"-"+kxChargingRecord.getChannelNo(),// 设备编号-插座号
                        kxChargingRecord.getMaxChargingPower().toString(),account.getBalance().toString(),account.getGive().toString(),// 最大功率、账户余额、赠送金额
                        DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,kxChargingRecord.getCreateTime()),
                        DateUtils.parseDateToStr(DateUtils.YYYY_MM_DD_HH_MM_SS,kxChargingRecord.getUpdateTime()),
                        charge_minutes.toString(),kxChargingRecord.getAmount().toString(),
                        Constants.EndTypeMap.get(kxChargingRecord.getEndType()));
            }
        }

    }
}
